package com.jungly.gridpasswordview;

import com.jungly.gridpasswordview.imebugfixer.ImeDelBugFixedEditText;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.KeyEvent;

/**
 * ●
 *
 * @author Jungly
 * jungly.ik@gmail.com
 * 15/3/5 21:30
 */
public class GridPasswordView extends DirectionalLayout implements PasswordView, Component.DrawTask {
    /**
     * DEFAULT_PASSWORD_LENGTH
     */
    public static final int DEFAULT_PASSWORD_LENGTH = 6;
    private static final int DEFAULT_LINE_WIDTH = 5;
    private static final int DEFAULT_MAX_PASSWORDLENGTH = 8;
    private static final int DEFAULT_TEXTSIZE = 16;
    private static final String DEFAULT_TRANSFORMATION = "●";
    private static final int DEFAULT_OUTER_LINE_COLOR = 0xaa888888;
    private static final int DEFAULT_TEXT_COLOR = Color.getIntColor("#ff000000");

    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");

    private Context mContext;
    private Color mTextColor;
    private int mTextSize = DEFAULT_TEXTSIZE;
    private int mLineWidth = DEFAULT_LINE_WIDTH;
    private Color mGridColor;
    private ShapeElement mOuterLineDrawable;
    private Color mOutLineColor;
    private int mPasswordLength;
    private String mPasswordTransformation;
    private int mPasswordType;
    private String[] mPasswordArr;
    private Text[] mViewArr;
    private TextField[] mViewFiledArr;
    private boolean showPassword = true;
    private ImeDelBugFixedEditText mInputView;
    private OnPasswordChangedListener mListener;
    private int mScreenWidth;
    private boolean isFirst = true;
    private boolean isDelInput = false;
    private Component.ClickedListener mOnClickListener = new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            forceInputViewGetFocus();
        }
    };

    /**
     * TextFiled 的文本输入监听
     */
    Text.TextObserver textWatcher = new ohos.agp.components.Text.TextObserver() {
        @Override
        public void onTextUpdated(String s, int index, int i1, int i2) {
            if (s == null || "".equals(s) || s.length() > mPasswordLength) {
                return;
            }
            for (int i = 0; i < mPasswordArr.length; i++) {
                String newNum = s.substring(s.length() - 1);
                if (mPasswordArr[i] == null) {
                    if (isDelInput) {
                        isDelInput = false;
                        break;
                    }
                    mPasswordArr[i] = newNum;
                    if (showPassword) {
                        mViewArr[i].setText(mPasswordTransformation);
                    } else {
                        mViewArr[i].setText(newNum);
                    }
                    notifyTextChanged();
                    break;
                }
            }
        }
    };

    Component.KeyEventListener KeyEventListener = new Component.KeyEventListener() {
        @Override
        public boolean onKeyEvent(Component component, KeyEvent keyEvent) {
            if (keyEvent.getKeyCode() == KeyEvent.KEY_SPACE) {
                return true;
            }
            if (keyEvent.isKeyDown() && keyEvent.getKeyCode() == KeyEvent.KEY_DEL) {
                for (int i = mPasswordArr.length - 1; i >= 0; i--) {
                    if (mPasswordArr[i] != null) {
                        mPasswordArr[i] = null;

                        mViewArr[i].setText(null);
                        notifyTextChanged();

                        isDelInput = true;
                        if (i == 0) {
                            isDelInput = false;
                            for (int j = mViewFiledArr.length - 1; j >= 0; j--) {
                                mViewFiledArr[j].setText(null);
                            }
                        }
                        break;
                    } else {
                        mViewArr[i].setText(null);
                    }
                }
            }
            return false;
        }
    };

    /**
     * constructor
     *
     * @param context context
     * @param attrs   attrs
     */
    public GridPasswordView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs, "");
    }

    /**
     * constructor
     *
     * @param context      context
     * @param attrs        attrs
     * @param defStyleAttr defStyleAttr
     */
    public GridPasswordView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttrSet attrs, String defStyleAttr) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        mScreenWidth = display.getAttributes().width;

        mContext = context;

        initAttrs(context, attrs, defStyleAttr);
        initViews(context);
    }

    private void initAttrs(Context context, AttrSet attrSet, String defStyleAttr) {
        mGridColor = TypedAttrUtils.getColor(attrSet, "gpv_grid_color", Color.BLACK);
        HiLog.info(LABEL, mGridColor.toString());
        mTextColor = TypedAttrUtils.getColor(attrSet, "gpv_text_color", new Color(DEFAULT_TEXT_COLOR));
        if (mTextColor == null) {
            mTextColor = new Color(DEFAULT_TEXT_COLOR);
        }
        showPassword = TypedAttrUtils.getBoolean(attrSet, "gpv_show_password", true);
        mOutLineColor = TypedAttrUtils.getColor(attrSet, "gpv_outer_line_color", new Color(DEFAULT_OUTER_LINE_COLOR));
        mPasswordType = TypedAttrUtils.getInteger(attrSet, "gpv_password_type", InputAttribute.PATTERN_NUMBER);

        int textSize = TypedAttrUtils.getDimensionPixelSize(attrSet, "gpv_text_size",
                AttrHelper.fp2px(DEFAULT_TEXTSIZE, context));
        if (textSize != -1) {
            this.mTextSize = AttrHelper.fp2px(DEFAULT_TEXTSIZE, context);
        }
        mLineWidth = TypedAttrUtils.getInteger(attrSet, "gpv_line_width", DEFAULT_LINE_WIDTH);
        if (mLineWidth < 1) {
            mLineWidth = DEFAULT_LINE_WIDTH;
        }
        mOuterLineDrawable = generateBackgroundDrawable();
        mPasswordLength = TypedAttrUtils.getInteger(attrSet, "gpv_line_width", DEFAULT_PASSWORD_LENGTH);
        mPasswordLength = attrSet.getAttr("gpv_password_length").get().getIntegerValue();
        if (mPasswordLength < 1) {
            mPasswordLength = DEFAULT_PASSWORD_LENGTH;
        }
        if (mPasswordLength > DEFAULT_MAX_PASSWORDLENGTH) {
            mPasswordLength = DEFAULT_MAX_PASSWORDLENGTH;
        }

        mPasswordTransformation = TypedAttrUtils.getString(attrSet,
                "gpv_password_transformation", DEFAULT_TRANSFORMATION);

        mPasswordArr = new String[mPasswordLength];
        mViewArr = new Text[mPasswordLength];
        mViewFiledArr = new TextField[mPasswordLength];
    }

    private void initViews(Context context) {
        super.setBackground(mOuterLineDrawable);
        setOrientation(HORIZONTAL);

        addDrawTask(this::onDraw);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (isFirst) {
            inflaterViews(mContext);
            isFirst = false;
        }
    }

    private void inflaterViews(Context context) {
        int compantWidth = (mScreenWidth - getMarginLeft() - getMarginRight()
                - getPaddingRight() - getPaddingLeft()) - mLineWidth * mPasswordLength;
        int singleCompantWidth = compantWidth / mPasswordLength;
        int index = 0;
        while (index < mPasswordLength) {
            if (index != 0) {
                Component line = new Component(context);
                line.setWidth(mLineWidth);
                line.setHeight(ComponentContainer.LayoutConfig.MATCH_PARENT);
                line.setBackground(generateDividerBackgroundColor());
                addComponent(line);
            }
            mInputView = (ImeDelBugFixedEditText) LayoutScatter.getInstance(context)
                    .parse(ResourceTable.Layout_gridpasswordview, null, false);
            mInputView.addTextObserver(textWatcher);
            mInputView.setKeyEventListener(KeyEventListener);
            mInputView.setTextInputType(mPasswordType);
            mInputView.setTruncationMode(Text.TruncationMode.AUTO_SCROLLING);

            mViewFiledArr[index] = mInputView;
            StackLayout stackLayout = new StackLayout(getContext());
            stackLayout.setWidth(singleCompantWidth);
            stackLayout.setHeight(110);
            stackLayout.addComponent(mInputView);
            Text text = new Text(context);
            text.setWidth(singleCompantWidth);
            text.setHeight(110);
            text.setTextColor(mTextColor);
            text.setTextAlignment(TextAlignment.CENTER);
            text.setTextSize(50);
            text.setTextAlignment(TextAlignment.HORIZONTAL_CENTER | TextAlignment.VERTICAL_CENTER);
            mViewArr[index] = text;
            stackLayout.addComponent(text);
            addComponent(stackLayout);
            index++;
        }
        setClickedListener(mOnClickListener);
    }

    private void setCustomAttr(Text view) {
        if (mTextColor != null) {
            view.setTextColor(mTextColor);
        }
        view.setTextSize(mTextSize);
    }

    private ShapeElement generateDividerBackgroundColor() {
        ShapeElement elementLine = new ShapeElement();
        elementLine.setRgbColor(RgbColor.fromArgbInt(mOutLineColor.getValue()));
        return elementLine;
    }

    private ShapeElement generateBackgroundDrawable() {
        ShapeElement element = new ShapeElement();
        element.setStroke(mLineWidth, RgbColor.fromArgbInt(mOutLineColor.getValue()));
        return element;
    }

    private void forceInputViewGetFocus() {
        mInputView.setFocusable(FOCUS_ENABLE);
        mInputView.setClickable(true);
        mInputView.setTouchFocusable(true);
        mInputView.requestFocus();
    }

    /**
     * 获取密码
     *
     * @return the text the PasswordView is displaying.
     */
    @Override
    public String getPassWord() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < mPasswordArr.length; i++) {
            if (mPasswordArr[i] != null) {
                sb.append(mPasswordArr[i]);
            }
        }
        return sb.toString();
    }

    private void notifyTextChanged() {
        if (mListener == null) {
            return;
        }
        String currentPsw = getPassWord();
        mListener.onTextChanged(currentPsw);

        if (currentPsw.length() == mPasswordLength) {
            mListener.onInputFinish(currentPsw);
        }
    }

    /**
     * Clear the passwrod the PasswordView is displaying.
     */
    @Override
    public void clearPassword() {
        for (int i = 0; i < mPasswordArr.length; i++) {
            mPasswordArr[i] = null;
            mViewArr[i].setText(null);
            mViewFiledArr[i].setText(null);
        }
    }

    /**
     * 设置密码
     *
     * @param password Sets the string value of the PasswordView.
     */
    @Override
    public void setPassword(String password) {
        clearPassword();

        if (password == null || password.length() == 0) {
            return;
        }

        char[] pswArr = password.toCharArray();
        for (int i = 0; i < pswArr.length; i++) {
            if (i < mPasswordArr.length) {
                mPasswordArr[i] = pswArr[i] + "";
                if (isShowPassword()) {
                    mViewArr[i].setText(mPasswordTransformation);
                } else {
                    mViewArr[i].setText(mPasswordArr[i]);
                }
            }
        }
    }

    /**
     * 设置密码明文密文
     *
     * @param visible Set the enabled state of this view.
     */
    @Override
    public void setPasswordVisibility(boolean visible) {
        this.showPassword = visible;
        setPassword(getPassWord());
    }

    @Override
    public void togglePasswordVisibility() {
        boolean currentVisible = getPassWordVisibility();
        setPasswordVisibility(!currentVisible);
    }

    /**
     * 密码是否显示
     *
     * @return Get the visibility of this view.
     */
    private boolean getPassWordVisibility() {
        return showPassword;
    }

    /**
     * 密码改变监听
     *
     * @param listener Register a callback to be invoked when password changed.
     */
    @Override
    public void setOnPasswordChangedListener(OnPasswordChangedListener listener) {
        this.mListener = listener;
    }

    /**
     * 密码展示类型
     *
     * @param passwordType set password show type
     */
    @Override
    public void setPasswordType(PasswordType passwordType) {
        int inputType = InputAttribute.PATTERN_NUMBER;
        switch (passwordType) {
            case TEXT:
                inputType = InputAttribute.PATTERN_TEXT;
                break;

            case NUMBER:
                inputType = InputAttribute.PATTERN_NUMBER;
                break;

            case PASSWORD:
                inputType = InputAttribute.PATTERN_PASSWORD;
                break;

            case PATTERN_NULL:
                inputType = InputAttribute.PATTERN_NULL;
                break;

            default:
                inputType = InputAttribute.PATTERN_NUMBER;
        }
        for (TextField textView : mViewFiledArr) {
            textView.setTextInputType(inputType);
        }
    }

    public boolean isShowPassword() {
        return showPassword;
    }

    /**
     * 密码是否显示
     *
     * @param showPassword 是否显示密码
     */
    public void setShowPassword(boolean showPassword) {
        this.showPassword = showPassword;
        setPassword(getPassWord());
    }

    /**
     * Interface definition for a callback to be invoked when
     * the password changed or is at the maximum length.
     */
    public interface OnPasswordChangedListener {
        /**
         * Invoked when the password changed.
         *
         * @param psw new text
         */
        void onTextChanged(String psw);

        /**
         * Invoked when the password is at the maximum length.
         *
         * @param psw complete text
         */
        void onInputFinish(String psw);
    }
}
